package com.zg.core.base

import android.content.Context
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator
import com.zg.core.ext.logI
import java.util.*

/***
 * @Author:      zxd
 * @CreateDate:  2021/7/6 14:12
 * @Description:
 */
@Navigator.Name("fragment")
class SHFragmentNavigator(
    val context: Context,
    val manager: FragmentManager,
    val containerId: Int
) :
    FragmentNavigator(
        context,
        manager,
        containerId
    ) {

    private val mBackStack = ArrayDeque<Int>()

    override fun navigate(
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        if (manager.isStateSaved) {
            logI(tag, "Ignoring navigate() call: FragmentManager has already saved its state")
        }

        var className = destination.className
        if (className[0] == '.') {
            className = context.packageName + className
        }

        val ft = manager.beginTransaction()

        var enterAnim = navOptions?.enterAnim ?: -1
        var exitAnim = navOptions?.exitAnim ?: -1
        var popEnterAnim = navOptions?.popEnterAnim ?: -1
        var popExitAnim = navOptions?.popExitAnim ?: -1
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = if (enterAnim != -1) enterAnim else 0
            exitAnim = if (exitAnim != -1) exitAnim else 0
            popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
            popExitAnim = if (popExitAnim != -1) popExitAnim else 0
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
        }
        //得到正在显示的fragment
        val fragment: Fragment? = manager.primaryNavigationFragment
        if (fragment != null) {
            if (destination.className == fragment.javaClass.name) return destination
            logI(
                tag,
                "正在显示的碎片:${fragment.javaClass.name} tag${fragment.tag} navigate${destination.className}"
            )
            ft.hide(fragment) //隐藏掉，才能显示下一个页面展示出来
        }

        //下一个页面
        var frag: Fragment? = null
        val tag = destination.id.toString()
        //通过tag寻找碎片
        frag = manager.findFragmentByTag(tag)
        if (frag != null) {
            logI("显示${frag.tag}")
            ft.show(frag)
        } else {
            logI("新创建${className}")
            frag = instantiateFragment(context, manager, className, args) //实例化
            frag.arguments = args //传参
            ft.add(containerId, frag, tag) //添加
        }
        ft.setPrimaryNavigationFragment(frag)

        @IdRes val destId = destination.id
        val initialNavigation = mBackStack.isEmpty()
        // TODO Build first class singleTop behavior for fragments
        val isSingleTopReplacement = (navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId)

        val isAdded: Boolean
        isAdded = if (initialNavigation) {
            true
        } else if (isSingleTopReplacement) {
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size > 1) {
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                manager.popBackStack(
                    generateBackStackName(mBackStack.size, mBackStack.peekLast()),
                    FragmentManager.POP_BACK_STACK_INCLUSIVE
                )
                ft.addToBackStack(generateBackStackName(mBackStack.size, destId))
            }
            false
        } else {
            ft.addToBackStack(generateBackStackName(mBackStack.size + 1, destId))
            true
        }
        if (navigatorExtras is Extras) {
            for ((key, value) in navigatorExtras.sharedElements) {
                ft.addSharedElement(key!!, value!!)
            }
        }
        ft.setReorderingAllowed(true)
        ft.commit()
        // The commit succeeded, update our view of the world
        return if (isAdded) {
            mBackStack.add(destId)
            destination
        } else {
            null
        }
        return super.navigate(destination, args, navOptions, navigatorExtras)
    }

    companion object {
        private const val tag = "SHFragmentNavigator"
    }

    private fun generateBackStackName(backStackindex: Int, destid: Int): String? {
        return "$backStackindex-$destid"
    }
}